#include "Utils.h"
#include <getopt.h>

#define VERSION "1.0.0"
#define EXIT_NOW 1

typedef enum{
	false = 0,
	true
} t_bool;

struct globalArgs_t {
	char *out_file;			/* output file */
	const char *outFileName;	/* -o option */
	char **inputNumbers;		/* input files */
	t_bool version;
	t_bool help;
	t_bool mcm;
	t_bool dcm;
	t_bool arg;
} globalArgs;

static const char *optString = "Vho:dm";

static const struct option longOpts[] = {
	{ "version", no_argument, NULL, 'V' },
	{ "help", no_argument, NULL, 'h' },
	{ "output", required_argument, NULL, 'o' },
	{ "multiple", no_argument, NULL, 'm' },
	{ "divisor", no_argument, NULL, 'd' },
	{0, 0, 0, 0}
};

/*
 * Imprime las opciones de uso en file_o
 */
void mostrar_ayuda (FILE* file_o){
	fprintf(stdout,
			"Usage:\n\n"
			"common [options] M N\n"
			"common - find Maximum Common Divisor and Minimum Common Multiple between two positive integer numbers\n\n"
			"Options:\n"
			"  -V, --version\tPrint version and quit.\n"
			"  -h, --help\tPrint this information and quit.\n"
			"  -o, --output\tSpecifies the output file. Specifie argument - for stdout\n"
			"  -m, --multiple\tShows only the MCM.\n"
			"  -d, --divisor\tShows only the MDC.\n");
}

/*
 * Imprime en file_o la version del programa
 */
void mostrar_version (FILE* file_o){
	fprintf(stdout,
			"common (TP1 1er cuatrimestre 2011) " VERSION "\n");
}

/**
 * Parsea las opciones que se reciben al invocar el programa
 */
int parsear_opciones(int argc, char* argv[], FILE **file_o) {
	int opt = 0;
	int longIndex = 0;
	
	opt = getopt_long( argc, argv, optString, longOpts, &longIndex );

	while( opt != -1 ) {
		switch( opt ) {
			case 'm':
				globalArgs.mcm = true;	
				globalArgs.arg = true;
				break;

			case 'd':
				globalArgs.dcm = true;
				globalArgs.arg = true;
				break;

			case 'o':
				globalArgs.outFileName = optarg;
				break;

			case 'V':
				globalArgs.version = true;
				break;

			case 'h':
				globalArgs.help = true;
				break;
				
			case '?':
				if (optopt == 'o' || 'm')
					return EXIT_FAILURE;
			default:
				break;
		}

		opt = getopt_long( argc, argv, optString, longOpts, &longIndex );
	}	

	globalArgs.inputNumbers = argv + optind;
	return EXIT_SUCCESS;
}

/**
 * Parsea los numeros a analizar.
 * Devuelve EXIT_SUCCESS en caso de exito y EXIT_FAILURE si no son validos
 */
int numbers(unsigned int *num_1, unsigned int *num_2) {
	char *endptr=NULL;
	long long auxll;
	void * auxptr;
	
	auxll = strtoll(globalArgs.inputNumbers[0], &endptr, 0);
	if (auxll < 2 || auxll > UINT_MAX || *endptr!='\0'){
		fprintf (stderr,"Error: Primer numero fuera de rango.\n");
		return EXIT_FAILURE;
	}
	*num_1=(unsigned int)auxll;
	
	auxll = strtoll(globalArgs.inputNumbers[1], &endptr, 0);
	if (auxll < 2 || auxll > UINT_MAX || *endptr!='\0'){
		fprintf (stderr,"Error: Segundo numero fuera de rango.\n");
		return EXIT_FAILURE;
	}
	*num_2=(unsigned int)auxll;

	return EXIT_SUCCESS;
}

int open_output_file(FILE ** file_o){
	  if (globalArgs.outFileName == NULL || strcmp(globalArgs.outFileName,"-") == 0){
		*file_o = stdout;
		}
	else {
		*file_o = fopen(globalArgs.outFileName, "wt");
		if (*file_o == NULL) {
			*file_o = stdout;
			return EXIT_FAILURE;
		}
	}
	return EXIT_SUCCESS;
}

/**
 * Inicializa el struct globalArgs
 */
void inicializar_arg() {
	globalArgs.out_file = NULL;
	globalArgs.inputNumbers = NULL;
	globalArgs.outFileName = NULL;
	globalArgs.version = false;
	globalArgs.help = false;
	globalArgs.mcm = false;	
	globalArgs.dcm = false;
	globalArgs.arg = false;	
}

 
int main (int argc, char* argv[]) {
	FILE *file_o;
	unsigned int num_1, num_2, num_int, valor_dcm, valor_mcm;
	
	inicializar_arg();

	if ( parsear_opciones(argc, argv, &file_o) ){
		return EXIT_FAILURE;
	}

	if (globalArgs.arg == false){
		globalArgs.mcm = true;
		globalArgs.dcm = true;
	}

	if (globalArgs.help) {
		mostrar_ayuda(file_o);
		return EXIT_SUCCESS;
	}

	if (globalArgs.version) {
		mostrar_version(file_o);
		return EXIT_SUCCESS;
	}

	if ((argc - optind) != 2) {
		fprintf (stderr,"Error: Se deben especificar dos numeros para comparar\n");
		return EXIT_FAILURE;
	}

	if (numbers(&num_1, &num_2)) {
		fprintf (stderr,"Error: No se pudo de utilizar alguno de los numeros especificados.\n Fuera de Rango o no valido\n");
		return EXIT_FAILURE;
	}

	if (open_output_file(&file_o)){
		fprintf (stderr,"Error: No se pudo de abrir el archivo de salida.\n");
		return EXIT_FAILURE;
	}	
	
	if (num_1 < num_2){
		num_int = num_2;
		num_2 = num_1;
		num_1 = num_int;
	}
	
	valor_dcm = dcm(num_1,num_2);
	valor_mcm = mcm(num_1,num_2);
		
	if (globalArgs.mcm == true){
		if (valor_mcm == 0)
			fprintf (stderr,"Error: El producto de los numeros se encuentra fuera de rango.\n");			
		else
			fprintf(file_o,"%u\n",valor_mcm);
	}	
	
	if (globalArgs.dcm == true)
		fprintf(file_o,"%u\n",valor_dcm);		
		
	return 0;
}

